package com.ht.api.db.task;

import java.lang.reflect.Constructor;
import java.util.concurrent.atomic.AtomicInteger;

import com.ht.api.db.Entity;
import com.ht.api.db.util.HtUtil;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

/**
 * 任务组
 * @author asq
 * @createTime 2024年12月10日 19:54:29
 */
@Data
@Accessors(chain = true)
@Slf4j
public class TaskGroup {
	/** 主键ID */
	private String id;
	/** 任务总数量 */
	private int total;
	/** 任务已完成数量 */
	private AtomicInteger done = new AtomicInteger(0);
	/** 任务开始时间 */
	private String startTime;
	/** 任务完成时间 */
	private String doneTime;
	/** 所有任务执行完后的回调 */
	private TaskCallback callback;
	/** 任务组的全局参数 */
	private JSONObject param;
	/** 任务组状态 */
	private Integer status = 0;
	/** 任务组名称 */
	private String name;

	private TaskGroup() {
		this.id = HtUtil.getId();
		this.startTime = DateUtil.now();
		this.name = "未命名";
	}
	
	/**
	 * 构造一个任务组
	 * @param total 任务总数
	 * @return
	 */
	public static TaskGroup create(int total) {
		return new TaskGroup().setTotal(total);
	}
	
	/**
	 * 构造一个任务组
	 * @param param 任务组参数
	 * @param total 任务总数
	 * @return
	 */
	public static TaskGroup create(JSONObject param, int total) {
		return new TaskGroup().setParam(param).setTotal(total);
	}

	/**
	 * 构造一个任务组
	 * @param total 任务总数
	 * @param callback 所有任务执行完后的回调
	 * @return
	 */
	public static TaskGroup create(int total, TaskCallback callback) {
		return new TaskGroup().setTotal(total).setCallback(callback);
	}
	
	/**
	 * 构造一个任务组
	 * @param param 任务组参数
	 * @param total 任务总数
	 * @param callback 所有任务执行完后的回调
	 * @return
	 */
	public static TaskGroup create(JSONObject param, int total, TaskCallback callback) {
		return new TaskGroup().setParam(param).setTotal(total).setCallback(callback);
	}

	/**
	 * 构造一个任务组，使用默认回调
	 * @param total 任务总数
	 * @return
	 */
	public static TaskGroup createToInsertBatchResult(int total) {
		return new TaskGroup().setTotal(total).setCallback(new TaskGroupToInsertBatchResultCallback());
	}
	
	/**
	 * 构造一个任务组，使用默认回调
	 * @param param 任务组参数
	 * @param total 任务总数
	 * @return
	 */
	public static TaskGroup createToInsertBatchResult(JSONObject param, int total) {
		return new TaskGroup().setParam(param).setTotal(total).setCallback(new TaskGroupToInsertBatchResultCallback());
	}

	/**
	 * 构造一个任务组，在默认回调的基础上，增加自定义回调<br>
	 * 注意：会先执行默认回调，再执行自定义回调
	 * @param total 任务总数
	 * @param callback 自定义回调类
	 * @return
	 */
	public static TaskGroup createToInsertBatchResult(int total, TaskCallback callback) {
		EnhanceTaskGroupToInsertBatchResultCallback myCallback = new EnhanceTaskGroupToInsertBatchResultCallback();
		myCallback.setMyCallback(callback);
		return new TaskGroup().setTotal(total).setCallback(myCallback);
	}
	
	/**
	 * 构造一个任务组，在默认回调的基础上，增加自定义回调<br>
	 * 注意：会先执行默认回调，再执行自定义回调
	 * @param param 任务组参数
	 * @param total 任务总数
	 * @param callback 自定义回调类
	 * @return
	 */
	public static TaskGroup createToInsertBatchResult(JSONObject param, int total, TaskCallback callback) {
		EnhanceTaskGroupToInsertBatchResultCallback myCallback = new EnhanceTaskGroupToInsertBatchResultCallback();
		myCallback.setMyCallback(callback);
		return new TaskGroup().setParam(param).setTotal(total).setCallback(myCallback);
	}
	
	/**
	 * 任务完成数+1
	 * @return 是否已完成任务组
	 */
	public boolean markDone() {
		// 如果全部任务已完成，则触发回调
		if (total == done.incrementAndGet()) {
			if (callback != null) {
				log.info("[taskGroup]所有任务完成，触发回调，groupId:{}, 任务数:{}", this.id, this.total);
				callback.callback(this.id, this.param);
			}
			this.doneTime = DateUtil.now();
			this.status = 1;
		} else {
			log.info("[taskGroup]完成任务+1，groupId:{}, 任务数:{}", this.id, this.total);
		}
		
		return this.done.get() == this.total;
	}
	
	/**
	 * 转化为Entity,便于数据库写入
	 * @return
	 */
	public Entity toEntry() {
		Entity entity = Entity.create("db_task_group")
				.setMainId(id)
				.set("param", param != null ? param.toString() : null)
				.set("total", total)
				.set("callback", callback != null ? callback.getClass().getName() : null)
				.set("done", done.get())
				.set("start_time", startTime)
				.set("status", status)
				.set("done_time", doneTime)
				.set("name", name)
				;
		return entity;
	}
	
	/**
	 * 从数据库Entity转化为TaskGroup实例
	 * @param entity
	 * @return
	 */
	public static TaskGroup load(Entity entity) {
		return new TaskGroup()
				.setId(entity.getStr("main_id"))
				.setParam(JSONUtil.parseObj(entity.getStr("param")))
				.setTotal(entity.getInt("total"))
				.setCallback(loadTaskGroup(entity.getStr("callback")))
				.setDone(new AtomicInteger(entity.getInt("done")))
				.setStartTime(entity.getStr("start_time"))
				.setStatus(entity.getInt("status"))
				.setDoneTime(entity.getStr("done_time"))
				.setName(entity.getStr("name"))
				;
	}
    
    /**
     * 根据类路径创建任务组
     * @param className 类的全限定名
     * @return 类的实例
     */
    public static TaskCallback loadTaskGroup(String className) {
    	if (StrUtil.isBlank(className)) {
    		return null;
    	}
    	
        try {
            Class<?> clazz = Class.forName(className);
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            return TaskCallback.class.cast(constructor.newInstance());
        } catch (Exception e) {
        	log.error("[task]无法创建任务组：{}", className);
        	return null;
        }
    }
}
